package netty.demo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import netty.handler.ServerHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

/**
 * 服务端启动引导代码
 */
public class NettyServer {

    private static final Logger log = LoggerFactory.getLogger(NettyServer.class.getSimpleName());

    public static void main(String[] args) throws InterruptedException {
        NettyServer server = new NettyServer();
        server.start(8080);
    }

    /**
     * 调用start方法启动server端，这部分是网络通信部分，与handler内的业务代码部分解耦，
     * 同时这段代码由于只涉及网络通信，基本不会改变，属于模版代码
     */
    public void start(int port) throws InterruptedException {
        ServerHandler serverHandler = new ServerHandler();
        // 使用NIO，则对应的EventLoop就是NioEventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            log.info("开始初始化ServerBootstrap");
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(group)
                    .channel(NioServerSocketChannel.class)// 设置Channel类型
                    .localAddress(new InetSocketAddress(port))
                    /*
                     当一个新的连接被接受时，一个新的子Channel会被创建，然后会把一开始创建的ServerHandler实例加入到该
                     Channel的ChannelPipeline中，这样在操作该Channel时发生的回调，就会由ServerHandler来处理。
                      */
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            log.info("接收到一个请求，创建Channel，host:" + ch.remoteAddress().getHostString());
                            ch.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture future = bootstrap.bind().sync();// 这里会同步处理绑定，也就是线程会一直阻塞在该处
            log.info("Channel阻塞在关闭处");
            future.channel().closeFuture().sync();// 获取服务器主线程的Channel的CloseFuture，然后会一直阻塞在该处，直到主Channel被关闭
            log.info("Channel已关闭");
        } finally {
            group.shutdownGracefully().sync();// 关闭EventLoop，释放所有资源
        }
    }
}
